package com.michael.retail.gateway.exception;

import com.michael.retail.commons.pojo.global.R;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.autoconfigure.web.ResourceProperties;
import org.springframework.boot.autoconfigure.web.reactive.error.AbstractErrorWebExceptionHandler;
import org.springframework.boot.web.error.ErrorAttributeOptions;
import org.springframework.boot.web.reactive.error.ErrorAttributes;
import org.springframework.cloud.gateway.support.NotFoundException;
import org.springframework.context.ApplicationContext;
import org.springframework.core.annotation.Order;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.codec.ServerCodecConfigurer;
import org.springframework.lang.NonNull;
import org.springframework.stereotype.Component;
import org.springframework.web.reactive.function.BodyInserters;
import org.springframework.web.reactive.function.server.*;
import org.springframework.web.server.MethodNotAllowedException;
import org.springframework.web.server.ResponseStatusException;
import reactor.core.publisher.Mono;

import java.net.ConnectException;
import java.util.Map;

/**
 * 类功能描述:
 * <pre>
 *   WebFlux 全局异常处理类
 * </pre>
 *
 * @author Michael
 * @version 1.0
 * @date 2021/7/3 16:12
 */
@Order(-2)
@Component
@Slf4j
public class GlobalExceptionHandler extends AbstractErrorWebExceptionHandler {

    public GlobalExceptionHandler(
            ErrorAttributes errorAttributes,
            ResourceProperties resourceProperties,
            ServerCodecConfigurer serverCodecConfigurer,
            ApplicationContext applicationContext
    ) {
        // 引用父类构造
        super(errorAttributes, resourceProperties, applicationContext);
        // 配置messageWriters
        this.setMessageWriters(serverCodecConfigurer.getWriters());
    }

    @Override
    protected RouterFunction<ServerResponse> getRoutingFunction(ErrorAttributes errorAttributes) {
        return RouterFunctions.route(
                RequestPredicates.all(),
                this::renderErrorResponse
        );
    }

    @NonNull
    private Mono<ServerResponse> renderErrorResponse(ServerRequest request) {
        return ServerResponse.status(getHttpStatus(request))
                             .contentType(MediaType.APPLICATION_JSON)
                             .body(BodyInserters.fromValue(getErrorResult(request)));
    }

    @NonNull
    private R getErrorResult(ServerRequest request) {

        Map<String, Object> errMap = getErrorAttributes(request, ErrorAttributeOptions.of(
                ErrorAttributeOptions.Include.BINDING_ERRORS,
                ErrorAttributeOptions.Include.EXCEPTION,
                ErrorAttributeOptions.Include.STACK_TRACE,
                ErrorAttributeOptions.Include.MESSAGE
        ));

        String exception = errMap.get("exception").toString();
        String trace = errMap.get("trace").toString();
        String errMsg = errMap.get("message").toString();

        log.error("发生异常,trace信息如下\n{}", trace);

        if (ResponseStatusException.class.getName().equals(exception)) {
            return R.failed("404");
        } else if (NumberFormatException.class.getName().equals(exception)) {
            return R.failed("参数不匹配");
        } else if (MethodNotAllowedException.class.getName().equals(exception)) {
            return R.failed("不支持的方式");
        } else if (NotFoundException.class.getName().equals(exception)) {
            return R.failed("维护中...");
        } else if (ConnectException.class.getName().equals(exception)) {
            return R.failed("维护中...");
        } else {
            log.error("未区分的异常\t" + exception);
            return R.failed(errMsg);
        }
    }

    @NonNull
    private HttpStatus getHttpStatus(ServerRequest request) {
        Map<String, Object> errMap = getErrorAttributes(request, ErrorAttributeOptions.defaults());
        int status = (int) errMap.get("status");
        return HttpStatus.valueOf(status);
    }
}
